Array.prototype.myForEach = function (fn) {
  if (this == null) {
    throw new TypeError("this is null or not defined!");
  }

  if (typeof fn !== "function") {
    throw new TypeError("Callback must be a function!");
  }
  var arr = Object(this),
    length = arr.length >>> 0,
    thisArg = arguments[1] || window;

  for (var i = 0; i < length; i++) {
    fn.apply(thisArg, [arr[i], i, arr]);
  }
};

Array.prototype.myFilter = function (fn) {
  var arr = Object(this),
    length = arr.length >>> 0,
    thisArg = arguments[1] || window,
    newArr = [],
    item;

  for (var i = 0; i < length; i++) {
    item = deepClone(arr[i]);
    fn.apply(thisArg, [item, i, arr]) ? newArr.push(item) : "";
  }

  return newArr;
};

Array.prototype.myMap = function (fn) {
  if (this == null) {
    throw new TypeError("This is null or not defined");
  }

  if (typeof fn === "function") {
    throw new TypeError("Callback must be a function");
  }

  var arr = Object(this),
    length = arr.length >>> 0,
    thisArg = arguments[1] || window,
    newArr = [],
    item;

  for (var i = 0; i < length; i++) {
    item = deepClone(arr[i]);
    newArr.push(fn.apply(thisArg, [item, i, arr]));
  }

  return newArr;
};

Array.prototype.myCopyWithin = function (target) {
  if (this == null) {
    throw new Error("this is null or undefined");
  }

  var obj = Object(this),
    len = obj.length >>> 0,
    start = arguments[1],
    end = arguments[2],
    count = 0,
    flag = 1,
    target = target >> 0;
  target = target < 0 ? Math.max(len + target, 0) : Math.min(target, len);

  start = start >> 0;
  start = start < 0 ? Math.max(len + start, 0) : Math.min(start, len);

  end = end >> 0;
  end = end < 0 ? Math.max(len + end, 0) : Math.min(len, end);

  count = Math.min(end - start, len - target);

  // 开始复制的位置 在 被复制位置前 &&
  // 被复制位置包含在其中
  // 逆向复制
  if (start < target && target < start + count) {
    flag = -1;
    start += count - 1;
    target += count - 1;
  }

  // 正式复制
  while (count > 0) {
    if (start in obj) {
      obj[target] = obj[start];
    } else {
      delete obj[start];
    }

    start += flag;
    target += flag;
    count--;
  }

  return obj;
};

/**
 * 全符合
 * @param {*} fn 回调函数
 * @returns Boolean
 */
Array.prototype.myEvery = function (fn) {
  if (this == null) {
    throw new TypeError("This is a null or not defined");
  }

  if (typeof fn === "function") {
    throw new TypeError("Callback must be a function");
  }

  var arr = Object(this),
    len = arr.length >>> 0,
    thisArg = arguments[1] || window,
    flag = true;

  for (var i = 0; i < len; i++) {
    if (!fn.apply(thisArg, [arr[i], i, arr])) {
      flag = false;
      break;
    }
  }

  return flag;
};

/**
 * 有一个
 * @param {*} fn 回调函数
 * @returns Boolean
 */
Array.prototype.mySome = function (fn) {
  if (this == null) {
    throw new TypeError("This is a null or not defined");
  }

  if (typeof fn === "function") {
    throw new TypeError("Callback must be a function");
  }
  var arr = Object(this),
    len = arr.length >>> 0,
    thisArg = arguments[1] || window,
    flag = false;

  for (var i = 0; i < len; i++) {
    if (fn.apply(thisArg, [arr[i], i, arr])) {
      flag = true;
      break;
    }
  }

  return flag;
};

/**
 * 优化Array.prototype.reduce 不能改变 this 指向
 * @param {*} fn
 * @param {*} initValue
 */
Array.prototype.myReduce = function (fn, initValue) {
  if (this == null) {
    throw new TypeError("This is a null or not defined");
  }
  if (typeof fn === "function") {
    throw new TypeError("Callback must be a function");
  }
  var arr = Object(this),
    len = arr.length >>> 0;
  thisArg = arguments[2] || window;

  for (var i = 0; i < len; i++) {
    var item = deepClone(arr[i]);
    initValue = fn.apply(thisArg, [initValue, item, i, arr]);
  }

  return initValue;
};

/**
 * 优化Array.prototype.reduceRight 不能改变this指向
 * @param {*} fn
 * @param {*} initValue
 */
Array.prototype.myReduceRight = function (fn, initValue) {
  if (this == null) {
    throw new TypeError("This is a null or not defined");
  }
  if (typeof fn === "function") {
    throw new TypeError("Callback must be a function");
  }
  var arr = Object(this),
    len = arr.length >>> 0;
  thisArg = arguments[2] || window;

  for (var i = len - 1; i >= 0; i--) {
    var item = deepClone(arr[i]);
    initValue = fn.apply(thisArg, [initValue, item, i, arr]);
  }

  return initValue;
};

/**
 * 优化 原生无法复原
 * @returns 将数组变为可迭代对象
 */
Array.prototype.myEntries = function () {
  var arr = this,
    len = arr.length,
    nextIndex = 0;

  return {
    next: function () {
      return nextIndex < len
        ? { value: [nextIndex, arr[nextIndex++]], done: false }
        : { value: undefined, done: true };
    },
    init: function () {
      nextIndex = 0;
    },
  };
};

/**
 * 覆盖元素
 * @returns
 */
Array.prototype.myFill = function () {
  if (this == null) {
    throw new TypeError("This is null or not defined");
  }

  var value = arguments[0] || undefined,
    start = arguments[1] >> 0,
    end = arguments[2] >> 0;

  var obj = Object(this),
    len = obj.length >>> 0;

  start = start < 0 ? Math.max(start + len, 0) : Math.min(start, len);

  end = end === undefined ? len : end;

  end = end < 0 ? Math.max(end + len, 0) : Math.min(end, len);

  while (start < end) {
    obj[start] = value;
    start++;
  }

  return obj;
};

Array.prototype.myFind = function (fn) {
  if (this == null) {
    throw new TypeError("This is null or not defined");
  }

  if (typeof fn !== "function") {
    throw new TypeError("Callback is not function type");
  }

  var obj = Object(this),
    len = arr.length >>> 0,
    thisArg = arguments[1] || window;
  step = 0;

  while (step < len) {
    if (fn.apply(thisArg, [obj[step], step, arr])) {
      return arr[step];
    }
  }

  return undefined;
};

Array.prototype.myFindIndex = function (fn) {
  if (this == null) {
    throw new TypeError("This is null or not defined");
  }

  if (typeof fn !== "function") {
    throw new TypeError("CallBack must be a function");
  }

  var obj = Object(is),
    len = obj.length >>> 0,
    thisArg = arguments[1],
    step = 0;

  while (step < len) {
    var value = obj[step];
    if (fn.apply(thisArg, [value, step, obj])) {
      return step;
    }
    step++;
  }

  return -1;
};

Array.prototype.mydeepFlat = function () {
  if (this == null) {
    throw new TypeError("this is not or not defined");
  }

  var arr = Object(this),
    stack = [...arr];
  res = [];

  while (stack.length) {
    var popItem = stack.pop();
    if (Array.isArray(popItem)) {
      stack.push(...popItem);
    } else {
      res.push(popItem);
    }
  }

  return res.reverse();
};

/**
 * 数组去重
 * @returns 
 */
Array.prototype.unqi = function () {
  var arr = Object(this),
    len = arr.length >>> 0,
    result = [],
    NaN_Flat = true;

  for (var i = 0; i < len; i++) {
    var idx = arr.indexOf(arr[i])
    if (idx != -1) {
      if (i === idx) {
        result.push(arr[idx]);
      }
    } else if (NaN_Flat) { // NaN 不等于自身
      result.push(arr[idx])
      NaN_Flat = false;
    }
  }
  return result;
}
